#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/select.h>
#include <unistd.h>

const static int PORT = 8080; 

int main()
{
    // 1. 创建网络套接字
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0) {
        perror("socket failed");
        return -1;
    }

    // 2. 开启端口复用
    int opt = 1;
    int n = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    if (n < 0) {
        perror("setsockopt failed");
        return -2;
    }

    // 3. 绑定
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(PORT);

    n = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
    if (n < 0) {
        perror("bind failed");
        return -3;
    }

    // 4. 监听
    n = listen(fd, 8); // 看看 select 下，backlog 参数如何传递
    if (n < 0) {
        perror("listen failed");
        return -4;
    }

    // 5. 初始化 fd_set
    fd_set master_set;
    FD_ZERO(&master_set); // 清空 master_set
    FD_SET(fd, &master_set); // 将监听套接字加入set

    int max_fd = fd;

    while (1) {
        fd_set working_set = master_set; // 每次循环复制 master_set（select 会修改它
        
        // 5. 调用select等待事件
        int activity = select(max_fd + 1, &working_set, NULL, NULL, NULL);
        if (activity < 0) {
            perror("select error");
            return -5;
        }

        // 6. 检查就绪 FD
        // 6.1 检查新连接（监听套接字可读）
        if (FD_ISSET(fd, &working_set)) {
            struct sockaddr_in address;
            memset(&address, 0, sizeof(address));
            socklen_t len = sizeof(address);
            int socket = accept(fd, (struct sockaddr*)&address, &len);
            if (socket < 0) {
                perror("accept error");
                return -6;
            }

            // 将新套接字加入 master_set
            FD_SET(socket, &master_set);
            if (socket > max_fd) {
                max_fd = socket; // 更新最大 FD
            }
        }

        char buffer[128] = {0};

        // 6.2 检查客户端数据（遍历所有可能 FD）
        for (int i = fd + 1; i <= max_fd; i++) {
            if (FD_ISSET(i, &working_set)) {
                int cnt = recv(i, buffer, 128, 0);
                buffer[cnt] = 0;
                // 连接出错或关闭
                if (cnt <= 0) {
                    close(i);
                    FD_CLR(i, &master_set);
                }
                
                printf("clientfd: %d, count: %d, buffer: %s\n", i, cnt, buffer);
                // 回显给客户端
                send(i, buffer, cnt, 0);
            }
        }

    }

    close(fd);
    

    return 0;
}